﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Serialization.Formatters;
using System.Text;
using SmartMathLibrary.Space;

namespace SmartMathLibrary.Space
{
    /// <summary>
    /// This class provides the creation of a local or network wide space object container.
    /// </summary>
    public class SpaceServerProvider : MarshalByRefObject
    {
        /// <summary>
        /// This field holds the name of the object space.
        /// </summary>
        private readonly String name;

        /// <summary>
        /// This field holds the http channel of the object space.
        /// </summary>
        private TcpChannel channel;

        /// <summary>
        /// This field holds a object reference of the object space.
        /// </summary>
        private ObjRef marshalObject;

        /// <summary>
        /// This field holds the object space.
        /// </summary>
        private readonly SpaceContainer container;

        /// <summary>
        /// Occurs when a space item was add to the object space.
        /// </summary>
        public event ItemAddedEventHandler ItemAdded;

        /// <summary>
        /// Occurs when a space item was deleted.
        /// </summary>
        public event ItemDeletedEventHandler ItemDeleted;

        /// <summary>
        /// Occurs when a space item got updated.
        /// </summary>
        public event ItemUpdatedEventHandler ItemUpdated;

        /// <summary>
        /// Initializes a new instance of the <see cref="SpaceServerProvider"/> class.
        /// </summary>
        /// <param name="name">The name of the object space in the network or on the local host.</param>
        /// <param name="container">The container to publish.</param>
        public SpaceServerProvider(String name, SpaceContainer container)
        {
            if (container == (SpaceContainer) null)
            {
                throw new ArgumentNullException("container");
            }

            this.name = name;
            this.container = container;
            this.container.ItemAdded += new ItemAddedEventHandler(SpaceServerProvider_ItemAdded);
            this.container.ItemDeleted += new ItemDeletedEventHandler(SpaceServerProvider_ItemDeleted);
            this.container.ItemUpdated += new ItemUpdatedEventHandler(SpaceServerProvider_ItemUpdated);
        }

        private void SpaceServerProvider_ItemUpdated(uint id)
        {
            ItemUpdatedEventHandler handler = this.ItemUpdated;

            if (handler != null)
            {
                handler(id);
            }
        }

        private void SpaceServerProvider_ItemDeleted(uint id)
        {
            ItemDeletedEventHandler handler = this.ItemDeleted;

            if (handler != null)
            {
                handler(id);
            }
        }

        private void SpaceServerProvider_ItemAdded(uint id)
        {
            ItemAddedEventHandler handler = this.ItemAdded;

            if (handler != null)
            {
                handler(id);
            }
        }

        /// <summary>
        /// Publishes the space to the network and the local host. The default port is the standard HTTP port 8080.
        /// </summary>
        public void PublishSpace()
        {
            this.PublishSpace(8080);
        }

        /// <summary>
        /// Publishes the object space to the network and the local host.
        /// </summary>
        /// <param name="port">The publishing port.</param>
        public void PublishSpace(int port)
        {
            IDictionary properties = new Hashtable();
            BinaryClientFormatterSinkProvider providerClient = new BinaryClientFormatterSinkProvider();
            BinaryServerFormatterSinkProvider providerServer = new BinaryServerFormatterSinkProvider();
           
            providerServer.TypeFilterLevel = TypeFilterLevel.Full;
            properties["port"] = port;
            properties["name"] = "spaceChannel:"+port.ToString();

            this.channel = new TcpChannel(properties, providerClient, providerServer);

            ChannelServices.RegisterChannel(channel, false);
            RemotingConfiguration.CustomErrorsMode = CustomErrorsModes.Off;

            this.marshalObject = RemotingServices.Marshal(this.container, this.name);
        }

        /// <summary>
        /// Unpublishes the object space. All client connections will be abrupt.
        /// </summary>
        public void UnpublishSpace()
        {
            ChannelServices.UnregisterChannel(this.channel);
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }
}